package xyz.lwm.lazycat.transaction.policy;

import xyz.lwm.lazycat.transaction.execute.TranDefinition;
import xyz.lwm.lazycat.transaction.execute.Transaction;
import xyz.lwm.lazycat.transaction.execute.TransactionManager;
import xyz.lwm.lazycat.utility.CollectionUtil;
import xyz.lwm.lazycat.utility.It;

/**
 * 始终以新事务的方式运行，若存在当前事务，则暂停（挂起）当前事务; <br>
 * 嵌套事务也复用该策略, 嵌套事务有父级事务
 *
 * @author lwm
 * @see xyz.lwm.lazycat.transaction.execute.Propagation#REQUIRES_NEW
 */
class RequiresNewPolicy extends AbstractPolicy {

    // 嵌套事务时, 使用父级事务来处理整体事务流程
    private final Transaction parent;

    /**
     * 新事物构造, 父级事务为 null
     *
     * @param tranDefinition 事务定义
     */
    public RequiresNewPolicy(TranDefinition tranDefinition) {
        this(tranDefinition, null);
    }

    /**
     * 嵌套事务构造函数:
     * 当父级事务开启, 则当前事务就是嵌套事务;
     * 父级事务不存在或未开启, 当前事务自动转为新事物策略
     *
     * @param tranDefinition 当前事务定义
     * @param parent 父级事务
     */
    RequiresNewPolicy(TranDefinition tranDefinition, Transaction parent) {
        super(tranDefinition);
        this.parent = It.of(parent).filter(Transaction::isOpen).getOr(null);
    }

    /**
     * 嵌套事务运行逻辑, 简言之, 回滚就立即, 提交则延后.
     */
    @Override
    public <T> T execute(CheckedSupplier<T> supplier) throws Throwable {
        TransactionManager.begin(tranDefinition);
        Transaction tran = TransactionManager.current();
        if (parent != null) {
            tran.setParent(parent);
        }
        try {

            T result = supplier.get();
            // 如父事务不存在, 则当前事务非嵌套事务, 可以直接提交
            if (parent == null) {
                // 嵌套事务要跟随父事务一起完成提交
                completeAll(tran, true);
            }
            // 如果当前事务是嵌套事务, 则不能直接提交, 须等待父事务协助完成提交
            return result;

        } catch (Throwable e) {

            // 当前异常信息是否需要回滚事务
            boolean rollbackOnly = isRollbackOnly(e, tranDefinition);
            // 需要回滚则回滚, 否则提交
            completeAll(tran, !rollbackOnly);
            throw e;

        } finally {
            // 事务节点结束时, 必须移除当前事务信息, 保证逻辑准确
            TransactionManager.end(tranDefinition);
        }
    }

    /**
     * 判断当前异常是否需要回滚事务。<br>
     * 默认回滚 RuntimeException及其子类、Error 及其子类;  <br>
     * 或通过 tranDefinition.getRollbackFor() 指定回滚异常列表,
     * 也可以用 {@link xyz.lwm.lazycat.transaction.Transactional} 指定回滚异常列表
     *
     * @param e              当前异常
     * @param tranDefinition 事务定义
     * @return true表示需要回滚事务
     */
    private boolean isRollbackOnly(Throwable e, TranDefinition tranDefinition) {
        Class<? extends Throwable>[] rollbackFor = tranDefinition.getRollbackFor();
        if (CollectionUtil.isEmpty(rollbackFor)) {
            return e instanceof RuntimeException || e instanceof Error;
        }
        // foreach and check rollback
        for (Class<? extends Throwable> clz : rollbackFor) {
            if (clz.isAssignableFrom(e.getClass())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 提交或回滚全部, 包括所有嵌套子节点事务
     *
     * @param transaction 当前事务
     * @param isCommit 是否提交 ? true: 提交, false: 回滚
     */
    private void completeAll(Transaction transaction, boolean isCommit) {
        // commit or rollback for itself
        if (isCommit) {
            transaction.commit();
        } else {
            transaction.rollback();
        }
        // commit or rollback for children
        if (CollectionUtil.isNotEmpty(transaction.children())) {
            for (Transaction child : transaction.children()) {
                if (!child.isCompleted()) {
                    completeAll(child, isCommit);
                }
            }
        }
    }

}
